﻿using OSGeo.GDAL;
using OSGeo.OGR;
using RemoteSensingImageDirectServer.Untils.Extensions;
using RemoteSensingImageDirectServer.Untils.ScopeSpace;
using RemoteSensingImageDirectServer.Untils.Tool;

namespace RemoteSensingImageDirectServer.Untils.Source
{
    public class SingleImageSource : IRSISingleImageSource, IRSIMapSource
    {
        /// <summary>
        /// 资源是否被释放
        /// </summary>
        private bool _disposed = false;

        /// <summary>
        /// 图层的路径
        /// </summary>
        private readonly string Filename;

        /// <summary>
        /// 图层的掩膜文件路径
        /// </summary>
        private string MaskFilename;

        /// <summary>
        /// 图层投影的目标坐标系的EPSG编码
        /// </summary>
        private readonly int EPSG;

        /// <summary>
        /// 数据的MD5值
        /// </summary>
        private readonly string Key;

        /// <summary>
        /// Vrt文件的数据流
        /// </summary>
        private byte[] VrtBytes;

        /// <summary>
        /// Warp文件的数据流
        /// </summary>
        private byte[]? WarpBytes;

        /// <summary>
        /// 是否正常初始化
        /// </summary>
        private readonly bool IsValid = false;

        /// <summary>
        /// 源数据坐标系数据
        /// </summary>
        private Dataset SrcDataset { get; set; }

        /// <summary>
        /// 投影后的坐标系数据
        /// </summary>
        private Dataset DstDataset { get; set; }

        /// <summary>
        /// 源数据坐标六参数
        /// </summary>
        private RasterEnvelope SrcRasterEnvelope { get; set; }

        /// <summary>
        /// 投影后数据的坐标系六参数
        /// </summary>
        private RasterEnvelope DstRasterEnvelope { get; set; }

        /// <summary>
        /// 掩膜文件的数据源
        /// </summary>
        private DataSource MaskDatasource { get; set; }

        public SingleImageSource(string key, string filename, int epsg)
        {
            Key = key;
            Filename = filename;
            EPSG = epsg;
            InitSrcDataset();
            IsValid = InitDstDataset();
        }


        public SingleImageSource(string key, string filename, string maskfilename, int epsg)
        {
            Key = key;
            Filename = filename;
            MaskFilename = maskfilename;
            EPSG = epsg;
            InitSrcDataset();
            IsValid = InitDstDataset();
        }

        public SingleImageSource(string key, string filename, byte[] vrtbytes, byte[]? warpbytes, string maskfilename, int epsg)
        {
            Key = key;
            Filename = filename;
            VrtBytes = vrtbytes;
            WarpBytes = warpbytes;
            MaskFilename = maskfilename;
            EPSG = epsg;
            InitSrcDataset();
            IsValid = LoadDstDataset();
        }


        private void InitSrcDataset()
        {
            if (Filename.ToLower().EndsWith(".jpg") || Filename.ToLower().EndsWith(".jpeg"))
            {
                SrcDataset = Gdal.OpenShared(Filename, Access.GA_ReadOnly);
            }
            else
            {
                SrcDataset = Gdal.OpenShared(Filename, Access.GA_Update);
            }
        }

        private bool InitDstDataset()
        {
            if (EPSG != 0)
            {
                var savepath = Path.GetDirectoryName(Filename);

                var warpFilename = string.Format(@"{0}\{1}_warp_{2}.vrt", savepath, Key, EPSG);
                var vrtFilename = string.Format(@"{0}\{1}_{2}.vrt", savepath, Key, EPSG);

                var warpDataset = GdalTool.ExecuteWarpByFile(warpFilename, Filename, EPSG);
                if (warpDataset == null) return false;
                warpDataset.Free();

                var vrtDataset = GdalTool.ExecuteVrtByFile(vrtFilename, warpFilename);
                if (vrtDataset == null) return false;
                vrtDataset.Free();

                WarpBytes = RSIFileTool.FileToByteArray(warpFilename);
                VrtBytes = RSIFileTool.FileToByteArray(vrtFilename);

                RSIFileTool.Delete(warpFilename);
                RSIFileTool.Delete(vrtFilename);

                var vsiVrtName = string.Format("/vsimem/{0}_{1}.vrt", Key, EPSG);
                var vsiWarpName = string.Format("/vsimem/{0}_warp_{1}.vrt", Key, EPSG);

                Gdal.FileFromMemBuffer(vsiWarpName, WarpBytes);
                Gdal.FileFromMemBuffer(vsiVrtName, VrtBytes);

                DstDataset = Gdal.OpenShared(vsiVrtName, Access.GA_Update);
            }
            else
            {
                var savepath = Path.GetDirectoryName(Filename);
                var vrtFilename = string.Format(@"{0}\{1}_{2}.vrt", savepath, Key, EPSG);
                var vrtDataset = GdalTool.ExecuteVrtByFile(vrtFilename, Filename);
                if (vrtDataset == null) return false;
                vrtDataset.Free();

                VrtBytes = RSIFileTool.FileToByteArray(vrtFilename);

                RSIFileTool.Delete(vrtFilename);

                var vsiVrtName = string.Format("/vsimem/{0}_{1}.vrt", Key, EPSG);
                Gdal.FileFromMemBuffer(vsiVrtName, VrtBytes);

                DstDataset = Gdal.OpenShared(vsiVrtName, Access.GA_Update);
            }
            return true;
        }

        private bool LoadDstDataset()
        {
            var vsiVrtName = string.Format("/vsimem/{0}_{1}.vrt", Key, EPSG);
            var vsiWarpName = string.Format("/vsimem/{0}_warp_{1}.vrt", Key, EPSG);

            if (WarpBytes != null) Gdal.FileFromMemBuffer(vsiWarpName, WarpBytes);

            if (VrtBytes != null) Gdal.FileFromMemBuffer(vsiVrtName, VrtBytes);

            DstDataset = Gdal.OpenShared(vsiVrtName, Access.GA_Update);
            return true;
        }

        private void InitMaskDatasource()
        {
            MaskDatasource = Ogr.OpenShared(MaskFilename, 0);
        }

        public string GetKey()
        {
            return this.Key;
        }

        public int GetDstEPSG()
        {
            return this.EPSG;
        }
        public Dataset GetDstDataset()
        {
            return this.DstDataset;
        }

        public RasterEnvelope GetDstRasterEnvelope()
        {
            return this.DstRasterEnvelope ??= this.DstDataset.GetRasterEnvelope();
        }

        public Dataset GetSrcDataset()
        {
            return this.SrcDataset;
        }

        public RasterEnvelope GetSrcRasterEnvelope()
        {
            return this.SrcRasterEnvelope ??= this.SrcDataset.GetRasterEnvelope();
        }

        public DataSource GetMaskDataSource()
        {
            return this.MaskDatasource;
        }

        public string GetMaskFilename()
        {
            return this.MaskFilename;
        }

        public bool Valid()
        {
            return this.IsValid;
        }

        public byte[] GetVrtBytes()
        {
            return this.VrtBytes;
        }

        public byte[]? GetWarpBytes()
        {
            return this.WarpBytes;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                if (disposing)
                {
                    DstDataset?.Free();
                    SrcDataset?.Free();

                    MaskDatasource?.Free();
                    SrcRasterEnvelope?.Dispose();
                    DstRasterEnvelope?.Dispose();

                    var vsiVrtName = string.Format("/vsimem/{0}_{1}.vrt", Key, EPSG);
                    var vsiWarpName = string.Format("/vsimem/{0}_warp_{1}.vrt", Key, EPSG);
                    Gdal.Unlink(vsiWarpName);
                    Gdal.Unlink(vsiVrtName);
                }

                this._disposed = true;
            }
            else
            {
                throw new Exception("资源已被释放");
            }
        }
        ~SingleImageSource()
        {
            Dispose(false);
        }
    }
}
